package web.classes;

import interfaces.SearchEngine;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import logger.ForumLogger;
import search.CompassSearchEngine;
import server.ForumServer;
import domain.items.ForumThread;
import domain.items.Item;
import domain.items.Post;
import domain.items.PostPacket;
import domain.items.ThreadPacket;
import domain.items.Topic;
import domain.items.TopicPacket;
import domain.users.Guest;
import domain.users.Member;
import domain.users.User;

public class WebHandler {

	/**
	 * each incoming client is associated with an instance of WebHandler
	 */

	/**
	 * the controller of the server (all instances of WsepHandler have reference
	 * to this single instance of the server)
	 */
	private ForumServer _server;

	/**
	 * this is the item being viewed by the surfer at this moment
	 */
	private Item _currentlyViewedItem;

	/**
	 * a Connection instance relates to a single user.
	 */
	private User _user;

	/**
	 * search engine for search queries
	 */
	private SearchEngine _searchEngine;

	/**
	 * constructor.
	 *
	 * @param acceptedSocket
	 * @param server
	 * @throws IOException
	 */
	public WebHandler(){

		this._server = ForumServer.getInstance();
		this._user = new Guest();
		_server.setNumOfCurrUsers(1);
		_server.getDataRepository().updateUserLoginStat();
		this._searchEngine = CompassSearchEngine.getInstance();
	}

	public boolean deleteUser(String username) {
		boolean success = this._server.getUserRepository().removeUser(username);

		return success;
	}

	/**
	 * search by date.  result is a stored as new ForumTread named "Search Result"
	 * the new thread is set to be the currently viewed item.
	 *
	 * @param fromDate
	 * @param toDate
	 */
	public void searchByDate(String fromDate, String toDate) {

		SimpleDateFormat tDateFormat = new SimpleDateFormat("dd/MM/yy");
		try {
			Date from = tDateFormat.parse(fromDate);
			Date to =  tDateFormat.parse(toDate);
					
			Vector<Post> ans = this._searchEngine.searchByDate(from,to);
			
			ForumThread searchThread = new ForumThread(this._currentlyViewedItem);
			searchThread.setName("Search Result");
			searchThread.setPosts(ans);
			this.setCurrentlyViewedItem(searchThread);
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			return;
		}
	}

	/**
	 * search by content. result is a stored as new ForumTread named "Search Result"
	 * the new thread is set to be the currently viewed item.
	 * 
	 * @param sentence
	 */
	public void searchByContent(String sentence) {

		Vector<Post> ans = this._searchEngine.searchByContent(sentence);
		ForumThread searchThread = new ForumThread(this._currentlyViewedItem);
		searchThread.setName("Search Result");
		searchThread.setPosts(ans);
		this.setCurrentlyViewedItem(searchThread);
	}

	/**
	 * search by author name. result is a stored as new ForumTread named "Search Result"
	 * the new thread is set to be the currently viewed item.
	 *
	 * @param authorName
	 */
	public void searchByAuthor(String authorName) {

		Vector<Post> ans = this._searchEngine.searchByAuthor(authorName);
		ForumThread searchThread = new ForumThread(this._currentlyViewedItem);
		searchThread.setName("Search Result");
		searchThread.setPosts(ans);
		this.setCurrentlyViewedItem(searchThread);
	}

	/**
	 * used to step backwards.
	 *
	 * @return serialized item
	 */
	public Item getFather() {
		Item father = this._currentlyViewedItem.getFather();
		if (father == null) {
			father = this._currentlyViewedItem;
		}
		else{
			setCurrentlyViewedItem(father);
		}
		Item toReturn;
		if (father instanceof Topic) {
			toReturn = ((Topic) father).convertToPacket();
		} else if (father instanceof ForumThread) {
			toReturn = ((ForumThread) father).convertToPacket();
		} else {
			toReturn = null;
		}
		this._currentlyViewedItem = father;
		return toReturn;
	}

	/**
	 * increments user rank by 1
	 *
	 * @param username
	 * @return
	 */
	public boolean promoteUser(String username) {
		boolean success = this._server.getUserRepository().changeRank(username,
				1);
		return success;
	}

	/**
	 * decrements user rank by 1
	 *
	 * @param username
	 * @return
	 */
	public boolean demoteUser(String username) {
		return this._server.getUserRepository().changeRank(username, -1);
	}

	/**
	 * adds a post to the current thread being viewed
	 *
	 * @param subject
	 * @param text
	 * @param userName
	 * @return
	 */
	public boolean addPost(String subject, String text) {
		Post newPost = new Post(this._currentlyViewedItem, getUsername(), subject,
				text);
		newPost.setDate(new Date());
		boolean success = this._server.getDataRepository().addPost(newPost);
		if (success) {
			this._searchEngine.updateSearchDB(newPost, null);
			@SuppressWarnings("unused")
			ThreadPacket temp = ((ForumThread) this._currentlyViewedItem)
					.convertToPacket();
			ForumLogger.logAddPost(getUsername(), subject);
			return true;
		} else {
			ForumLogger.logAddPostFail(getUsername(), subject);
			return false;
		}
	}

	/**
	 * adds a thread to the current topic being viewed
	 *
	 * @param newMessageSubject
	 * @param newMessageText
	 * @param username
	 */
	public boolean addThread(String newMessageSubject, String newMessageText) {
		ForumThread tempThread = new ForumThread(this._currentlyViewedItem);
		tempThread.setName(newMessageSubject);
		Post newPost = new Post(tempThread, getUsername(), newMessageSubject,
				newMessageText);
		this._server.getDataRepository().addThread(tempThread);
		this._searchEngine.updateSearchDB(newPost, null);
		if (this._server.getDataRepository().addPost(newPost)) {
			// TODO ForumLogger.logAddThread..... to ocmplete
			return true;
		} else {
			// TODO log failure...
			return false;
		}
	}

	/**
	 * delete the current post being viewed
	 */
	public boolean deletePost() {

		String tSubject = this._currentlyViewedItem.getName();
		Post oldPost = ((Post) (this._currentlyViewedItem)).clone();
		if (this._server.getDataRepository().deletePost(
				(Post) this._currentlyViewedItem)) {
			this._searchEngine.updateSearchDB(null, oldPost);
			ForumLogger.logDeletePost(((Member) this._user).getUserName(),
					tSubject);
			return true;
		} else {
			ForumLogger.logDeletePostFail(((Member) this._user).getUserName(),
					tSubject);
			return false;
		}
	}

	/**
	 * get a sub-item according to choice number
	 *
	 * @param choice
	 * @return serialized item
	 */
	public Item getItem(int choice) {
		Item item = _currentlyViewedItem;
		if (item.getType() == domain.items.Item.TOPIC) {
			item = ((Topic) item).getSubItems().get(choice - 1);
			this.setCurrentlyViewedItem(item);
			if (item.getType() == domain.items.Item.TOPIC) {
				TopicPacket packet = ((Topic) item).convertToPacket();
				return packet;
			}
			ThreadPacket packet = ((ForumThread) item).convertToPacket();
			return packet;
		} else if (item.getType() == domain.items.Item.FORUM_THREAD) {
			item = ((ForumThread) item).getPosts().get(choice - 1);
			this.setCurrentlyViewedItem(item);
			PostPacket packet = ((Post) item).convertToPacket();
			return packet;
		} else {
			System.out.println("WTF");
			return null;
		}
	}


	/**
	 * gets the root topic item
	 *
	 * @return serialized item
	 */
	public Item getRootTopic() {
		this._currentlyViewedItem = this._server.getDataRepository()
				.getRootTopic();
		TopicPacket packet = ((Topic) this._currentlyViewedItem)
				.convertToPacket();
		return packet;
	}

	public SearchEngine getSearchEngine() {
		return this._searchEngine;
	}

	/**
	 * @param userName
	 * @param password
	 * @return
	 */
	public User login(String userName, String password) {

		if (this._server.getUserRepository().authenticate(userName, password)) {
			this._user = this._server.getUserRepository().getUser(userName);
			// ****logging:****
			ForumLogger.logLogin(userName);
			// ****************
			return _user;
		} else {
			ForumLogger.logLoginFail(userName);
			return null;
		}
	}

	/**
	 * switch to 'guest mode' surfing
	 */
	public void logout() {
		// ****logging:****
		ForumLogger.logLogout(((Member) this._user).getUserName());
		setCurrentlyViewedItem(this._server.getDataRepository()
		.getRootTopic());
		// ****************
		this._user = new Guest();
	}

	/**
	 * add user to the repository
	 *
	 * @param username
	 * @param password
	 * @return
	 */
	public boolean registerUser(String username, String email, String password) {
		if (this._server.getUserRepository().addUser(username, email, password)) {
			ForumLogger.logRegisterUser(username);
			return true;
		} else {
			ForumLogger.logRegisterFail(username);
			return false;
		}
	}

	/**
	 * change the user's password
	 *
	 * @param username
	 * @param oldPass
	 * @param newPass
	 * @return
	 */
	public boolean updateUser(String username, String newEmail,
			String oldPass, String newPass) {
		return this._server.getUserRepository().updateUser(username, newEmail,
				oldPass, newPass);
	}

	/**
	 * modifies an existing post
	 *
	 * @param newSubject
	 * @param newText
	 * @return
	 */
	public boolean updatePost(String newSubject, String newText) {
		boolean reply;
		String oldSubject = ((Post) this._currentlyViewedItem).getName();
		Post oldPost = ((Post) (this._currentlyViewedItem)).clone();
		((Post) this._currentlyViewedItem).setName(newSubject);
		((Post) this._currentlyViewedItem).setContent(newText);
		if (this._server.getDataRepository().updatePost(
				(Post) this._currentlyViewedItem)) {
			reply = true;
			this._searchEngine.updateSearchDB(
					((Post) this._currentlyViewedItem), oldPost);
			ForumLogger.logUpdatePost(((Member) this._user).getUserName(),
					newSubject, oldSubject);
		} else {
			reply = false;
			ForumLogger.logUpdatePostFail(((Member) this._user).getUserName(),
					newSubject, oldSubject);
		}
		return reply;
	}
	
	/**
	 * Statistic method
	 * number of posts written by this user for each day for the past month.
	 * @param username - to get statistic for
	 * @return a chart for this user
	 */
	public void getStatForUser(String username) {
		this._server.getDataRepository().generateStat(1,username);
	}

	/**
	 * Statistic method
	 * Average number of posts written by all users for each hour of the day.
	 * @return a chart
	 */
	public void getStat_PostHours() {
		this._server.getDataRepository().generateStat(2,"");
	}

	/**
	 * Statistic method
	 * Average number of users that was online for each hour of the day.
	 * @return a chart
	 */
	public void getStat_UserHours() {
		this._server.getDataRepository().generateStat(3,"");
	}

	public Item getCurrentlyViewedItem() {
		return _currentlyViewedItem.convertToPacket();
	}

	public void setCurrentlyViewedItem(Item viewedItem) {
		_currentlyViewedItem = viewedItem;
	}


	public int getUserRank() {
		return this._user.getRank();
	}


	public String getUsername(){
		if(getUserRank()>0){
		return ((Member)this._user).getUserName();
		}
		else return null;
	}

	public void sayGoodbye(){
		this._server.setNumOfCurrUsers(-1);
	}

	public String getStatus(){
		return this._server.getCurrUsersStatus();
	}

}
